#pragma once

// 包含必要的头文件
#include <iostream>
#include <string>
#include <unistd.h>     // Unix标准函数定义
#include <cstring>      // 字符串处理
#include <sys/types.h>  // 基本系统数据类型
#include <sys/stat.h>   // 文件状态
#include <sys/socket.h> // 套接字定义
#include <arpa/inet.h>  // IP地址转换函数
#include <netinet/in.h> // Internet地址族
#include "Log.hpp"      // 日志类

// 错误码枚举
enum
{
    SocketErr = 2,  // socket创建错误
    BindErr,        // 绑定错误
    ListenErr,      // 监听错误
};

// 监听队列长度
const int backlog = 10;

// 套接字类，封装基本的网络操作
class Sock
{
public:
    Sock()
    {
    }
    ~Sock()
    {
    }

public:
    // 创建并初始化套接字
    void Socket()
    {
        // 创建TCP套接字
        // 参数1 AF_INET: 使用IPv4协议族
        // 参数2 SOCK_STREAM: 使用TCP协议（流式套接字）
        // 参数3 0: 使用默认协议
        sockfd_ = socket(AF_INET, SOCK_STREAM, 0);

        // 错误处理：检查套接字是否创建成功
        if (sockfd_ < 0)  // 小于0表示创建失败
        {
            // 记录致命错误日志
            // strerror(errno): 将错误码转换为可读的错误消息
            // errno: 系统的错误码
            lg(Fatal, "socker error, %s: %d", strerror(errno), errno);
            // 退出程序，返回错误码
            exit(SocketErr);
        }

        // 设置套接字选项
        int opt = 1;  // 选项值，1表示启用
        // SOL_SOCKET: 套接字层面的选项
        // SO_REUSEADDR: 允许重用本地地址
        // SO_REUSEPORT: 允许重用端口
        // 这两个选项用于防止服务器重启时出现"地址已被使用"的错误
        setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        //允许服务器程序快速重启
        //允许多个进程监听同一个端口
        //避免出现"Address already in use"错误
    }

    // 将套接字绑定到指定端口
    // 参数port: 要绑定的端口号（如80, 8080等）
    void Bind(uint16_t port)
    {
        // 创建IPv4地址结构体
        struct sockaddr_in local;
        // 将地址结构体清零，避免脏数据
        memset(&local, 0, sizeof(local));

        // 设置地址族为IPv4
        local.sin_family = AF_INET;           

        // 设置端口号
        // htons: 将主机字节序(Host)转换为网络字节序(Network)
        // 因为不同计算机的字节序可能不同（大端/小端）
        local.sin_port = htons(port);         

        // 设置IP地址
        // INADDR_ANY: 表示绑定所有网卡的IP地址
        // 如果服务器有多个网卡，可以接受发往任意网卡的请求
        local.sin_addr.s_addr = INADDR_ANY;   

        // 执行绑定操作
        // sockfd_: 要绑定的套接字
        // (struct sockaddr *)&local: 转换为通用地址结构体
        // sizeof(local): 地址结构体的大小
        if (bind(sockfd_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            // 绑定失败，记录错误日志
            lg(Fatal, "bind error, %s: %d", strerror(errno), errno);
            // 退出程序，返回绑定错误码
            exit(BindErr);
        }
    }

    // 开始监听连接请求
    // 将套接字设置为监听状态，准备接受客户端连接
    void Listen()
    {
        // listen系统调用
        // sockfd_: 要监听的套接字描述符
        // backlog: 连接队列的最大长度（在Socket.hpp中定义，默认值为10）
        // backlog指定了处于半连接状态(SYN_RCVD)和全连接状态(ESTABLISHED)的队列总和的最大值
        if (listen(sockfd_, backlog) < 0)
        {
            // 监听失败，记录致命错误日志
            // strerror(errno): 获取错误描述
            // errno: 错误码
            lg(Fatal, "listen error, %s: %d", strerror(errno), errno);
            // 退出程序，返回监听错误码
            exit(ListenErr);
        }
    }

    // 接受客户端的连接请求
    // 参数说明：
    // clientip: 指向string的指针，用于存储客户端的IP地址
    // clientport: 指向uint16_t的指针，用于存储客户端的端口号
    // 返回值：成功返回新连接的文件描述符，失败返回-1
    int Accept(std::string *clientip, uint16_t *clientport)
    {
        // 定义sockaddr_in结构体，用于存储客户端地址信息
        struct sockaddr_in peer;
        // 地址结构体的长度
        socklen_t len = sizeof(peer);

        // 接受新的连接
        // sockfd_: 监听套接字
        // &peer: 用于存储客户端地址信息的结构体
        // &len: 地址结构体的长度
        // accept()会阻塞等待，直到有新的连接到来
        int newfd = accept(sockfd_, (struct sockaddr*)&peer, &len);

        // 错误处理
        if(newfd < 0)
        {
            // 接受连接失败，记录警告日志
            lg(Warning, "accept error, %s: %d", strerror(errno), errno);
            return -1;
        }

        // 获取客户端IP地址和端口
        char ipstr[64];  // 临时缓冲区，存储IP地址字符串
        // inet_ntop: 将网络字节序的IP地址转换为点分十进制字符串
        // AF_INET: IPv4协议
        // &peer.sin_addr: 客户端IP地址
        inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr));
        
        // 将IP地址和端口信息存储到传入的指针中
        *clientip = ipstr;  // 保存IP地址
        *clientport = ntohs(peer.sin_port);  // 将网络字节序端口转换为主机字节序

        // 返回新连接的文件描述符
        return newfd;
    }

    // 连接到指定的服务器
    // 参数说明：
    // ip: 服务器的IP地址（如"127.0.0.1"）
    // port: 服务器的端口号（如8080）
    // 返回值：连接成功返回true，失败返回false
    bool Connect(const std::string &ip, const uint16_t &port)
    {
        // 创建服务器地址结构体
        struct sockaddr_in peer;
        // 清零地址结构体，避免垃圾数据
        memset(&peer, 0, sizeof(peer));

        // 设置地址族为IPv4
        peer.sin_family = AF_INET;

        // 设置端口号
        // htons将主机字节序转换为网络字节序
        peer.sin_port = htons(port);

        // 将IP地址字符串转换为网络字节序的二进制形式
        // inet_pton: 将点分十进制的IP地址转换为网络字节序
        // ip.c_str(): 将string转换为C风格字符串
        inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));

        // 尝试连接服务器
        // sockfd_: 客户端套接字
        // (struct sockaddr*)&peer: 服务器地址信息
        // sizeof(peer): 地址结构体大小
        int n = connect(sockfd_, (struct sockaddr*)&peer, sizeof(peer));

        // 错误处理
        if(n == -1) 
        {
            // 连接失败，输出错误信息
            std::cerr << "connect to " << ip << ":" << port << " error" << std::endl;
            return false;
        }

        // 连接成功
        return true;
    }

    // 关闭套接字
    void Close()
    {
        close(sockfd_);
    }

    // 获取套接字文件描述符
    int Fd()
    {
        return sockfd_;
    }

private:
    int sockfd_;    // 套接字文件描述符
};